// 递归搜索与回溯 - 决策树问题
// 当一个题目可以使用决策树画出来，那么也可以通过递归的方法解决
// 画决策树，要保证不重不漏，实际上就是暴搜
// 使用全局变量进行统计，避免递归函数头传参问题
// 设计递归函数头，是否需要记录本次决策的位置，层数，个数等信息
// 回溯时注意本层计算完成后，直接在本层回溯，返回上一个位置
// 经典题目：全排列，子集

// 例题 12：
// 编写一个程序，通过填充空格来解决数独问题。
//
//        数独的解法需 遵循如下规则：
//
//        数字 1-9 在每一行只能出现一次。
//        数字 1-9 在每一列只能出现一次。
//        数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。（请参考示例图）
//        数独部分空格内已填入了数字，空白格用 '.' 表示。
//
//        示例 1：
//
//
//        输入：board = [["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]]
//        输出：[["5","3","4","6","7","8","9","1","2"],["6","7","2","1","9","5","3","4","8"],["1","9","8","3","4","2","5","6","7"],["8","5","9","7","6","1","4","2","3"],["4","2","6","8","5","3","7","9","1"],["7","1","3","9","2","4","8","5","6"],["9","6","1","5","3","7","2","8","4"],["2","8","7","4","1","9","6","3","5"],["3","4","5","2","8","6","1","7","9"]]
//        解释：输入的数独如上图所示，唯一有效的解决方案如下所示：
//
//        提示：
//
//        board.length == 9
//        board[i].length == 9
//        board[i][j] 是一位数字或者 '.'
//        题目数据 保证 输入数独仅有一个解


// 解题思路：
// 如果决策树的分支不能成功，就返回 false（在中间过程中就有可能返回 false）, 同时需要回溯（恢复现场）
// 如果决策树能够成功， 返回 true（只有递归到最后一层，带能知道是否成功，返回 true 就说明已经都填完了）
// 如果都遍历完了，也没有返回 true 或者 false，就表示给的数组本身就是一个解完的数独，可以直接返回 true
public class SolveSudoku {
    boolean[][] row;
    boolean[][] col;
    boolean[][][] grid;

    public void solveSudoku(char[][] board) {
        row = new boolean[9][10];
        col = new boolean[9][10];
        grid = new boolean[3][3][10];

        // 初始化标记数组
        for(int i = 0; i < 9; i++){
            for(int j = 0; j < 9; j++){
                if(board[i][j] != '.'){
                    int num = board[i][j] - '0';
                    row[i][num] = true;
                    col[j][num] = true;
                    grid[i / 3][j / 3][num] = true;
                }
            }
        }

        dfs(board);
    }

    public boolean dfs(char[][] board){
        // 解数独 - 采用递归的思路
        for(int i = 0; i < 9; i++){
            for(int j = 0; j < 9; j++){
                if(board[i][j] == '.'){
                    for(int k = 1; k <= 9; k++){
                        if(!row[i][k] && !col[j][k] && !grid[i / 3][j / 3][k]){
                            board[i][j] = (char)('0' + k);
                            row[i][k] = true;
                            col[j][k] = true;
                            grid[i / 3][j / 3][k] = true;

                            if(dfs(board)) return true;// 重点理解

                            board[i][j] = '.';
                            row[i][k] = false;
                            col[j][k] = false;
                            grid[i /3][j / 3][k] = false;
                        }
                    }
                    // 如果这里不返回，程序就会找下一个空位置，把这个位置跳过去了，这样是不合理的
                    return false;// 重点理解
                }
            }
        }
        return true; // 重点理解
    }
}
